#include "tlm/tlmTSocket.hpp"

TargetSocket::TargetSocket(
    tlmModule      &parent 
    , sc_module_name  module_name
    , Addr high_address
    )
:sc_module(module_name),
socket("socket"),
maxAddress(high_address)
{
    assert(maxAddress>=0); // if m_length < 1, program will be aborted at here
    addr8Block  = (uint8_t*)malloc(maxAddress+1);
    addr16Block = (uint16_t*)addr8Block; 
    addr32Block = (uint32_t*)addr8Block;
    memset(addr8Block, 0, maxAddress+1);
    socket.register_b_transport       (this, &TargetSocket::custom_b_transport);
    socket.register_transport_dbg     (this, &TargetSocket::debug_transport);
}

void  TargetSocket::custom_b_transport(tlm_generic_payload &payload, sc_time &delay_time)
{
    Addr            address     = payload.get_address();     // memory address
    tlm_command     command     = payload.get_command();     // memory command
    uint8_t           *data     = (uint8_t*)payload.get_data_ptr();    // data pointer
    uint32_t           bytes    = payload.get_data_length(); // data length

    tlm_response_status response_status = checkPayloadAddress(payload)
            ? TLM_OK_RESPONSE
            : TLM_ADDRESS_ERROR_RESPONSE;

    if (payload.get_byte_enable_ptr()) {
        payload.set_response_status(TLM_BYTE_ENABLE_ERROR_RESPONSE);
    }
    else if (payload.get_streaming_width() != payload.get_data_length()) {
        payload.set_response_status(TLM_BURST_ERROR_RESPONSE);
    }

    if(response_status == TLM_OK_RESPONSE) {

        switch (command)
        {
            case TLM_WRITE_COMMAND: memcpy(&addr8Block[address], data, bytes);                 break;
            case TLM_READ_COMMAND:  memcpy(data, &addr8Block[address], bytes);                  break;
            default:                response_status = TLM_COMMAND_ERROR_RESPONSE; break;
        }
    }
    payload.set_response_status(response_status);
    if(command==TLM_WRITE_COMMAND)
    {
        gpayload.address = address;
        gpayload.bytes   = bytes;
        data_ready.notify(SC_ZERO_TIME);
    } 
}


Uns32 TargetSocket::debug_transport (tlm_generic_payload &payload)
{

    Addr           address  = payload.get_address();     // memory address
    tlm_command    command  = payload.get_command();     // memory command
    Uns8          *data     = payload.get_data_ptr();    // data pointer
    Uns32          bytes    = payload.get_data_length(); // data length
    bool           ok       = checkPayloadAddress(payload);

    if(ok) {
        switch (command)
        {
            default:                
                ok = false;                        
                break;
            case TLM_WRITE_COMMAND: 
                ok = true; 
                memcpy(&addr8Block[address], data, bytes);  
                break;
            case TLM_READ_COMMAND:  
                ok = true; 
                memcpy(data, &addr8Block[address], bytes);   
                break;
        }
    }
    if(command==TLM_WRITE_COMMAND)
    {
        gpayload.address = address;
        gpayload.bytes   = bytes;
        data_ready.notify(SC_ZERO_TIME);
    }
    return ok;
}

// void TargetSocket::mwrite(Addr address, uint8_t *data, uint32_t bytes)
// {
//     switch (bytes)
//     {
//     case 4:
        
//         break;
    
//     default:
//         break;
//     }
// }

bool TargetSocket::checkAddressRange ( Addr address, Addr bytes) {
            if  ( address + bytes - 1 > maxAddress ) {
                return 0;
            } else {
                return 1;
            }
        }

bool TargetSocket::checkPayloadAddress (tlm_generic_payload &gp) {
    return checkAddressRange(gp.get_address(), gp.get_data_length());
}